package com.tuananh.radnom.protocols.snmp;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Vector;

import org.snmp4j.AbstractTarget;
import org.snmp4j.CommunityTarget;
import org.snmp4j.PDU;
import org.snmp4j.ScopedPDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.TransportMapping;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.Integer32;
import org.snmp4j.smi.Null;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;

public class SnmpService {

	public static AbstractTarget createTarget(int version, String agentAddress,
			int retries, long timeout, String... secureInfos) {
		AbstractTarget target = null;
		if (secureInfos != null && secureInfos.length != 0)
			switch (version) {
			case SnmpConstants.version1:
			case SnmpConstants.version2c:
				target = new CommunityTarget();
				target.setAddress(GenericAddress.parse(agentAddress));
				// if the given agentAddress has wrong format
				if (target.getAddress() != null) {
					((CommunityTarget) target).setCommunity(new OctetString(
							secureInfos[0]));
					if (secureInfos.length>1) ((CommunityTarget) target).setCommunity(new OctetString(
							secureInfos[1]));
					target.setRetries(retries);
					target.setVersion(version);
					target.setTimeout(timeout);
				}
				break;
			case SnmpConstants.version3:

				break;
			}
		return target;
	}

	static PDU generatePDU(int version, String... OIDs) {
		PDU pdu;
		switch (version) {
		case SnmpConstants.version3:
			pdu = new ScopedPDU();
			break;
		default:
			pdu = new PDU();
		}
		for (String item : OIDs) {
			OID oid = new OID(item);
			pdu.add(new VariableBinding(oid));
		}
		return pdu;
	}

	public static Snmp createSnmp() {
		Snmp snmp = null;
		try {
			TransportMapping transport = new DefaultUdpTransportMapping();
			snmp = new Snmp(transport);
			USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(
					MPv3.createLocalEngineID()), 0);
			SecurityModels.getInstance().addSecurityModel(usm);
			transport.listen();
		} catch (IOException e) {
		}
		return snmp;
	}

	public static ArrayList<String> getByOIDs(int getType,
			ArrayList<String> OIDs, int version, String agentAddress,
			int retries, long timeout, String... secureInfos)
			throws IOException {
		ArrayList<String> result = new ArrayList<String>();
		Target target = createTarget(version, agentAddress, retries, timeout,
				secureInfos);
		Snmp snmp = createSnmp();
		snmp.listen();
		if (target != null && snmp != null) {
			ResponseEvent responseEvent = null;
			PDU pdu = generatePDU(target.getVersion(),
					OIDs.toArray(new String[OIDs.size()]));
			pdu.setType(getType);
			try {
				responseEvent = snmp.send(pdu, target);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			if (responseEvent != null) {
				PDU responsePDU = responseEvent.getResponse();
				if (responsePDU != null) {
					ArrayList<String> temp = new ArrayList<String>();
					Vector<? extends VariableBinding> vector = responsePDU
							.getVariableBindings();
					for (VariableBinding variable : vector) {
						if (!variable.isException())
							result.add(variable.toString());
						temp.add(variable.toString());
					}
				}
			}
		}
		return result;
	}

	public static ArrayList<String> snmpWalk(String oid, int version,
			String agentAddress, int retries, long timeout,
			String... secureInfos) throws IOException {
		Target target = createTarget(version, agentAddress, retries, timeout,
				secureInfos);
		Snmp snmp = createSnmp();
		if (target != null && snmp != null) {
			ArrayList<String> result = new ArrayList<String>();
			PDU requestPDU = generatePDU(target.getVersion(), oid);

			requestPDU.setType(PDU.GETNEXT);

			OID targetOID = new OID(oid);

			boolean finished = false;

			while (!finished) {
				VariableBinding vb = null;

				PDU responsePDU = snmp.send(requestPDU, target).getResponse();
				if (responsePDU != null) {

					vb = responsePDU.get(0);
				}

				if (responsePDU == null) {
					// System.out.println("responsePDU == null");
					finished = true;
				} else if (responsePDU.getErrorStatus() != 0) {
					// System.out.println("responsePDU.getErrorStatus() != 0");
					// System.out.println(responsePDU.getErrorStatusText());
					finished = true;
				} else if (vb.getOid() == null) {
					// System.out.println("vb.getOid() == null");
					finished = true;
				} else if (vb.getOid().size() < targetOID.size()) {
					// System.out.println("vb.getOid().size() < targetOID.size()");
					finished = true;
				} else if (targetOID.leftMostCompare(targetOID.size(),
						vb.getOid()) != 0) {
					// System.out.println("targetOID.leftMostCompare() != 0");
					finished = true;
				} else if (Null.isExceptionSyntax(vb.getVariable().getSyntax())) {

					// System.out.println(
					// "Null.isExceptionSyntax(vb.getVariable().getSyntax())");
					finished = true;
				} else if (vb.getOid().compareTo(targetOID) <= 0) {
					// System.out.println("Variable received is not "+
					// "lexicographic successor of requested "+
					// "one:");
					finished = true;

				} else {
					// Dump response.
					result.add(vb.toString());
					// Set up the variable binding for the next entry.
					requestPDU.setRequestID(new Integer32(0));
					requestPDU.set(0, vb);
				}
			}
			return result;
		}
		return null;
	}

	public static ArrayList<String> broadcastGetOIDs(MySnmpListener listener,
			ArrayList<String> OIDs, String broadcastAddress, int version,
			 int retries, long timeout,
			String... secureInfos) {
		String targetAddress = "udp:" + broadcastAddress + "/161";
		AbstractTarget target = createTarget(version, targetAddress, retries,
				timeout, secureInfos);
		Snmp snmp = createSnmp();
		if (target != null && snmp != null) {
			PDU pdu = generatePDU(version, (String[]) OIDs.toArray());
			pdu.setType(PDU.GET);
			try {
				// send an asynchronous request to the given broadcast
				// address
				snmp.send(pdu, target, null, listener);

				long startTime = Calendar.getInstance().getTimeInMillis();
				// wait for responses
				while (!listener.isFinished()
						&& Calendar.getInstance().getTimeInMillis() - startTime <= timeout) {
				}
				// Stop the MyBroadCastListener thread
				if (!listener.finished)
					listener.setFinished(true);

			} catch (IOException e) {
				// TODO Auto-generated catch block
				return null;
			}
		}
		return listener.results;
	}

	public static int check(int version, String agentAddress,
			int retries, long timeout, String... secureInfos)
			throws IOException {
		ArrayList<String> oids = new ArrayList<String>();
		oids.add(".1.3.6.1.2.1.1.1.0");
		ArrayList<String> result = getByOIDs(PDU.GET, oids, version,
				agentAddress, retries, timeout, secureInfos);
		for(String str:result){
			System.out.println(str);
		}
		if (result == null || result.size() < 1)
			return 0;
		return result.size();
	}

	public static boolean isSupportBridge(String address, int version,
			String agentAddress, int retries, long timeout,
			String... secureInfos) throws IOException {
		ArrayList<String> oids = new ArrayList<String>();
		oids.add(".1.3.6.1.2.1.17.1.2.0");

		ArrayList<String> results = getByOIDs(PDU.GET, oids, version,
				agentAddress, retries, timeout, secureInfos);
		if (!results.isEmpty()) {
			oids.clear();
			oids.add(".1.3.6.1.2.1.17.2.6.0");
			results = getByOIDs(PDU.GET, oids, version, agentAddress, retries,
					timeout, secureInfos);
			if (!results.isEmpty()) {
				oids.clear();
				oids.add(".1.3.6.1.2.1.17.2.7.0");
				results = getByOIDs(PDU.GET, oids, version, agentAddress,
						retries, timeout, secureInfos);
				if (!results.isEmpty())
					return true;
			}
		}

		return false;
	}

	public static boolean isSupportPrinter(String address, int version,
			String agentAddress, int retries, long timeout,
			String... secureInfos) throws IOException {
		ArrayList<String> oids = new ArrayList<String>();
		oids.add(".1.3.6.1.2.1.43.5.1.1.1.1");

		ArrayList<String> results = getByOIDs(PDU.GET, oids, version,
				agentAddress, retries, timeout, secureInfos);
		if (!results.isEmpty())
			return true;
		return false;
	}

	public static abstract class MySnmpListener implements ResponseListener {

		boolean finished = false;

		protected ArrayList<String> results = new ArrayList<String>();

		public boolean isFinished() {
			return finished;
		}

		public void setFinished(boolean finished) {
			this.finished = finished;
		}

	}

	/**
	 * This class is used for asynchronous polling
	 * 
	 * @author SilentSkyTA
	 * 
	 */
	public static class MyBroadcastIPListener extends MySnmpListener {

		public void onResponse(ResponseEvent event) {

			if (event.getPeerAddress() != null) {
				// System.out.println(event.getPeerAddress().toString());
				String str = event.getPeerAddress().toString();
				str = str.subSequence(0, str.indexOf('/')).toString();
				results.add(str);
			}
			if (event.getResponse() == null) {
				finished = true;
			}
		}
	};

	public static class MyBroadcastValueListener extends MySnmpListener {

		public void onResponse(ResponseEvent event) {

			if (event.getPeerAddress() != null) {
				// System.out.println(event.getPeerAddress().toString());

				for (VariableBinding variable : event.getResponse()
						.getVariableBindings()) {
					String str = variable.toString();
					if (str != null)
						results.add(str);
				}
			}
			if (event.getResponse() == null) {
				finished = true;
			}
		}
	};
}
